package com.example.netty.handler;

import com.alibaba.fastjson.JSON;
import com.example.netty.bean.DeviceMsg;
import com.example.netty.bean.ResMsg;
import com.example.netty.enums.MsgTypeEnum;
import com.example.netty.exception.ErrorMessageException;
import com.example.netty.util.SessionUtils;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.SimpleChannelInboundHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.InetSocketAddress;

/**
 * @Author : lichong
 * @description :
 * @Date Create in 14:57 2020/1/5
 * @Modified By :
 **/
public class MyChannelInboundHandler extends SimpleChannelInboundHandler<DeviceMsg> {
    private static final Logger logger = LoggerFactory.getLogger(MyChannelInboundHandler.class);




    /**
     * 异常次数
     */
    private int exception_num;

    /**
     * 信息错误数量连续3次则关闭连接
     */
    private static final int MAX_BAD_MESSAGE = 3;

    @Override
    protected void channelRead0(ChannelHandlerContext ctx, DeviceMsg msg) throws Exception {
        SessionUtils.putSession(ctx, msg);
        SessionUtils.writeMsg(msg);
        exception_num = 0;
        logger.info("获取解码后数据：{}", JSON.toJSONString(msg));
        ctx.writeAndFlush(new ResMsg(MsgTypeEnum.SUCCESS, msg.getSnNo(), "SUCCESS"));
    }

    /**
     * 客户端与服务端 断连时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        super.channelInactive(ctx);
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        //断开连接时，必须关闭，否则造成资源浪费，并发量很大情况下可能造成宕机
        ctx.close();
        SessionUtils.remove();
        logger.info("chanel inactive ip:{}, port:{}", insocket.getAddress(), insocket.getPort());
    }

    /**
     * 客户端与服务端第一次建立连接时 执行
     *
     * @param ctx
     * @throws Exception
     */
    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        super.channelActive(ctx);
        ctx.channel().read();
        InetSocketAddress insocket = (InetSocketAddress) ctx.channel().remoteAddress();
        //此处不能使用ctx.close()，否则客户端始终无法与服务端建立连接
        logger.info("channel connect... ip:{}, port:{}", insocket.getAddress(), insocket.getPort());
    }

    /**
     * 当出现 Throwable 对象才会被调用，即当 Netty 由于 IO 错误或者处理器在处理事件时抛出的异常时
     *
     * @param ctx
     * @param cause
     * @throws Exception
     */
    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        //抛出异常，断开与客户端的连接
        logger.error("处理异常：", cause);
        if (cause.getCause() instanceof ErrorMessageException) {
            //每次异常就加一
            exception_num++;
            ctx.writeAndFlush(new ResMsg(MsgTypeEnum.ERROR_MSG, "000000000000", "badMessage"));
            if (exception_num >= MAX_BAD_MESSAGE) {
                ctx.close();
            }
        } else {
            ctx.close();
        }
    }
}
